#include <gtest/gtest.h>
#include "myheader.cpp"
template <class T> void printSet(const set<T> test)
{
for (T x : test)
        cout << x << " ";
    cout << endl;
}

template <> void printSet(const set<CFG_Node*> test)
{
for(CFG_Node* x : test)
        cout << "Node " << x->CFGid << " ";
    cout << endl;
}

ostream& operator << (ostream &os , const CFG_Node *node)
{
    os << "node:" << node->CFGid;
}

TEST(set , op)
{
    set<int> a = {1 , 2 , 3, 4};
    set<int> b = {2 , 3 , 4, 5};
    set<int> resultUnion = {1 , 2 , 3 , 4 , 5};
    EXPECT_EQ(setUnion(a , b) , resultUnion);
    set<int> resultDiff = {1};
    EXPECT_EQ(setDiff(a , b) , resultDiff);
    set<int> resultIntr = {2 , 3 , 4};
    EXPECT_EQ(setIntr(a , b) , resultIntr);
}

TEST(CFG_Node , classTest)
{
    CFG_Node test;
    code testCode;
    testCode.codeLine = "a = a + b";
    testCode.def.insert("a");
    testCode.use.insert("a");
    testCode.use.insert("b");
    test.addCode(testCode);
    EXPECT_EQ(test.isDef("a") , true);
    EXPECT_EQ(test.isDef("b") , false);
    EXPECT_EQ(test.isUse("b") , true);
    int var = 0;
    test.renameVar("a" , 0 , var);
    EXPECT_EQ(test.codes[0].codeLine , string("a1 = a0 + b"));
}

TEST(CFG_Node , addEdge)
{
    CFG_Node test[3];
    test[0].addOutEdge(&test[2]);
    test[1].addOutEdge(&test[2]);
    test[2].addOutEdge(&test[0]);
    CFG_Node::edgeList result;
    result.push_back(&test[0]);
    result.push_back(&test[1]);
    EXPECT_EQ(result , test[2].gIn());
}

TEST(CFG_Node , domin)
{
    CFG_Node test[8];
    test[1].addOutEdge(&test[2]);
    test[2].addOutEdge(&test[3]);
    test[2].addOutEdge(&test[4]);
    test[3].addOutEdge(&test[5]);
    test[3].addOutEdge(&test[6]);
    test[5].addOutEdge(&test[7]);
    test[6].addOutEdge(&test[7]);
    test[7].addOutEdge(&test[2]);

    set<CFG_Node*> fullset;
for (auto &x : test)
    {
        fullset.insert(&x);
    }
for (auto &x : test)
    {
        x.dominSet = fullset;
    }

    test[1].computeDomin();
    set<CFG_Node*> dominResult;
    dominResult.insert(&test[2]);
    dominResult.insert(&test[1]);
    EXPECT_EQ(dominResult ,test[2].dominSet);
    EXPECT_EQ(test[1].CFGid    ,test[2].dominImm->CFGid);
    EXPECT_EQ(test[2].CFGid    ,test[3].dominImm->CFGid);
    EXPECT_EQ(test[2].CFGid    ,test[4].dominImm->CFGid);
    EXPECT_EQ(test[3].CFGid    ,test[5].dominImm->CFGid);
    EXPECT_EQ(test[3].CFGid    ,test[6].dominImm->CFGid);
    EXPECT_EQ(test[3].CFGid    ,test[7].dominImm->CFGid);
}

TEST(CFG_Node , DF)
{
    CFG_Node test[14];
    test[1].addOutEdge(test + 2);
    test[1].addOutEdge(test + 5);
    test[1].addOutEdge(test + 9);
    test[2].addOutEdge(test + 3);
    test[3].addOutEdge(test + 4);
    test[4].addOutEdge(test + 13);
    test[5].addOutEdge(test + 6);
    test[5].addOutEdge(test + 7);
    test[6].addOutEdge(test + 4);
    test[6].addOutEdge(test + 8);
    test[7].addOutEdge(test + 8);
    test[7].addOutEdge(test + 12);
    test[8].addOutEdge(test + 13);
    test[9].addOutEdge(test + 10);
    test[9].addOutEdge(test + 11);
    test[10].addOutEdge(test + 12);
    test[11].addOutEdge(test + 12);
    test[12].addOutEdge(test + 13);
    set<CFG_Node*> fullset;
for (auto &a : test)
        fullset.insert(&a);
for (auto &a : test)
        a.dominSet = fullset;
    test[1].computeDomin();
    test[1].computeDF();
    set<CFG_Node*> resultSet;
    resultSet.insert(test + 4);
    resultSet.insert(test + 13);
    resultSet.insert(test + 12);
    EXPECT_EQ(resultSet , test[5].DF);
}
class CFGTest : public ::testing::Test {
protected:
    virtual void SetUp()
    {
        for (int i = 0 ; i != 20 ; ++i)
            test_.addNode(CFG_Node());
        test_.linkNode(1 , 2);
        test_.linkNode(1 , 5);
        test_.linkNode(1 , 9);
        test_.linkNode(2 , 3);
        test_.linkNode(3 , 4);
        test_.linkNode(4 , 13);
        test_.linkNode(5 , 6);
        test_.linkNode(5 , 7);
        test_.linkNode(6 , 8);
        test_.linkNode(6 , 4);
        test_.linkNode(7 , 8);
        test_.linkNode(7 , 12);
        test_.linkNode(8 , 13);
        test_.linkNode(9 , 10);
        test_.linkNode(9 , 11);
        test_.linkNode(9 , 12);
    }

    CFG test_;
};

TEST_F(CFGTest , addNode)
{
    CFG_Node::edgeList result;
    result.push_back(&(test_.getNode(1)));
    EXPECT_EQ(result , test_.getNode(5).gIn());
    EXPECT_EQ(1 , test_.getNode(1).CFGid);
}

TEST_F(CFGTest , computeDomin)
{
    test_.computeDomin();
    EXPECT_EQ(&test_.getNode(9) , test_.getNode(10).dominImm);
}

TEST_F(CFGTest , computeDF)
{
    test_.computeDF();
    set<CFG_Node*> result;
    result.insert(&test_.getNode(5));
    result.insert(&test_.getNode(1));
    result.insert(&test_.getNode(8));
    EXPECT_EQ(result , test_.getNode(8).dominSet);
}

TEST_F(CFGTest , rangeTest)
{
    int count = 0;
for (CFG_Node &node : test_)
    {
        EXPECT_EQ(count , node.CFGid);
        count++;
    }
}

TEST(ssa , ssa)
{
    CFG test;
    code testCode;
    //Code block None
    CFG_Node blockNull;
    test.addNode(blockNull);
    //Code block 1
    CFG_Node block1;

    testCode.codeLine = "i = 1";
    testCode.def.insert("i");
    block1.addCode(testCode);

    testCode = code();
    testCode.codeLine = "j = 1";
    testCode.def.insert("j");
    block1.addCode(testCode);

    testCode = code();
    testCode.codeLine = "k = 0";
    testCode.def.insert("k");
    block1.addCode(testCode);

    test.addNode(block1);
    //Code block 2
    CFG_Node block2;
    testCode = code();
    testCode.codeLine = "if k < 100";
    testCode.use.insert("k");
    block2.addCode(testCode);
    test.addNode(block2);

    //Code block 3
    CFG_Node block3;
    testCode = code();
    testCode.codeLine = "if j < 20";
    testCode.use.insert("j");
    block3.addCode(testCode);
    test.addNode(block3);

    //Code block 4
    CFG_Node block4;
    testCode = code();
    testCode.codeLine = "return j";
    testCode.use.insert("j");
    block4.addCode(testCode);
    test.addNode(block4);

    //Code block 5
    CFG_Node block5;

    testCode = code();
    testCode.codeLine = "j = 1";
    testCode.def.insert("j");
    block5.addCode(testCode);

    testCode = code();
    testCode.codeLine = "k = k + 1";
    testCode.def.insert("k");
    testCode.use.insert("k");
    block5.addCode(testCode);
    test.addNode(block5);

    //Code block 6
    CFG_Node block6;

    testCode = code();
    testCode.codeLine = "j = k";
    testCode.def.insert("j");
    testCode.use.insert("k");
    block6.addCode(testCode);

    testCode = code();
    testCode.codeLine = "k = k + 2";
    testCode.def.insert("k");
    testCode.use.insert("k");
    block6.addCode(testCode);

    test.addNode(block6);

    //Code block 7
    CFG_Node block7;
    test.addNode(block7);

    test.linkNode(1 , 2);
    test.linkNode(2 , 3);
    test.linkNode(2 , 4);
    test.linkNode(3 , 5);
    test.linkNode(3 , 6);
    test.linkNode(5 , 7);
    test.linkNode(6 , 7);
    test.linkNode(7 , 2);

    test.ssa();
    test.printCFG(cout);
}

int main(int argc , char **argv)
{
    ::testing::InitGoogleTest(&argc , argv);
    return RUN_ALL_TESTS();
}
